/**

Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016.  All rights reserved.

Contact:
     SYSTAP, LLC DBA Blazegraph
     2501 Calvert ST NW #106
     Washington, DC 20008
     licenses@blazegraph.com

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
/*
 * Created on Sep 15, 2011
 */

package com.bigdata.rdf.sparql.ast;

import java.util.Map;

import org.openrdf.model.Value;

import com.bigdata.bop.BOp;
import com.bigdata.bop.BOpUtility;
import com.bigdata.bop.IBindingSet;
import com.bigdata.bop.PipelineOp;
import com.bigdata.rdf.lexicon.BigdataValueCentricFullTextIndex;
import com.bigdata.rdf.sail.sparql.ast.SimpleNode;
import com.bigdata.rdf.sparql.ast.optimizers.IASTOptimizer;

/**
 * A super container for the AST.
 * 
 * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
 * @version $Id$
 */
public class ASTContainer extends ASTBase {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    public interface Annotations extends QueryBase.Annotations {

        /**
         * The original query from which this AST was generated.
         */
        String QUERY_STRING = "queryString";

        /**
         * The parse tree generated from the query string (optional). For the
         * default integration, this is the parse tree assembled by the Sesame
         * <code>sparql.jjt</code> grammar. Other integrations may produce
         * different parse trees using different object models.
         * <p>
         * Note: There is no guarantee that the parse tree is a serializable
         * object. It may not need to be stripped off of the {@link QueryRoot}
         * if the {@link QueryRoot} is persisted or shipped to another node in a
         * cluster.
         */
        String PARSE_TREE = "parseTree";

        /**
         * The AST as received from the parser. This is either a
         * {@link QueryRoot} or an {@link UpdateRoot}.
         */
        String ORIGINAL_AST = "originalAST";

        /**
         * The AST as rewritten by the {@link IASTOptimizer}s.
         */
        String OPTIMIZED_AST = "optimizedAST";

        /**
         * The physical query plan (pipeline bops).
         */
        String QUERY_PLAN = "queryPlan";

        /**
         * The incoming binding set associated with the query plan, as
         * resulting from the query optimization phase.
         */
        String OPTIMIZED_AST_BINDING_SETS = "optimizedASTBindingSets";

        /**
         * The parsing time for the query (nanoseconds).
         */
        String QUERY_PARSE_TIME = "queryParseTime";
        
        /**
		 * The time to batch resolve RDF {@link Value}s in the query to
		 * {@link BigdataValue}s in the database (nanoseconds).
		 */
        String RESOLVE_VALUES_TIME = "resolveValuesTime";

		/**
		 * Flag which indicates completed resolution of IVs.
		 * Used to prevent running resolution again on consequent calls to query.evaluate
		 */
        String RESOLVED = "resolved";
    }

    /**
     * Deep copy constructor.
     */
    public ASTContainer(final ASTContainer op) {
        
        super(op);
        
    }

    /**
     * Shallow copy constructor.
     */
    public ASTContainer(final BOp[] args, final Map<String, Object> annotations) {

        super(args, annotations);
        
    }

    public ASTContainer(final QueryRoot queryRoot) {
        
        super(BOp.NOARGS, null/*anns*/);
        
        setOriginalAST(queryRoot);
        
    }

    public ASTContainer(final UpdateRoot updateRoot) {
        
        super(BOp.NOARGS, null/*anns*/);
        
        setOriginalUpdateAST(updateRoot);
        
    }
    
    /**
     * Return the original SPARQL QUERY -or- UPDATE from which this AST model was
     * generated.
     * 
     * @return The original Query or Update -or- <code>null</code> if the AST
     *         was not generated by the parser.
     */
    public String getQueryString() {

        return (String) getProperty(Annotations.QUERY_STRING);

    }

    /**
     * Set the SPARQL QUERY -or- UPDATE string used to generate the AST model.
     * 
     * @param queryString
     *            The query string.
     */
    public void setQueryString(final String queryString) {
        
        setProperty(Annotations.QUERY_STRING, queryString);

    }

    /**
     * Return the parse tree generated from the query string. 
     */
    public Object getParseTree() {

        return getProperty(Annotations.PARSE_TREE);
        
    }

    /**
     * Set the parse tree generated from the query string.
     * 
     * @param parseTree
     *            The parse tree (may be <code>null</code>).
     */
    public void setParseTree(final Object parseTree) {
        
        setProperty(Annotations.PARSE_TREE, parseTree);
        
    }

    /**
     * Return <code>true</code> iff this {@link ASTContainer} models a SPARQL
     * UPDATE operation.
     */
    public boolean isUpdate() {
        
        return getProperty(Annotations.ORIGINAL_AST) instanceof UpdateRoot;
        
    }

    /**
     * Return <code>true</code> iff this {@link ASTContainer} models a SPARQL
     * QUERY operation.
     */
    public boolean isQuery() {
        
        return getProperty(Annotations.ORIGINAL_AST) instanceof QueryRoot;
        
    }

    /**
     * Return the original AST model (before any optimization).
     */
    public UpdateRoot getOriginalUpdateAST() {

        return (UpdateRoot) getProperty(Annotations.ORIGINAL_AST);

    }

    /**
     * Set the original AST model (before any optimizations).
     */
    public void setOriginalUpdateAST(final UpdateRoot updateRoot) {
        
        setProperty(Annotations.ORIGINAL_AST, updateRoot);

    }

    /**
     * Return the original AST model (before any optimization).
     */
    public QueryRoot getOriginalAST() {

        return (QueryRoot) getProperty(Annotations.ORIGINAL_AST);

    }

    
    /**
     * Set the incoming binding sets associated with the optimized AST.
     */
    public void setOptimizedASTBindingSets(final IBindingSet[] bindingSets) {
        
        setProperty(Annotations.OPTIMIZED_AST_BINDING_SETS, bindingSets);

    }
    
    /**
     * Return the incoming binding sets associated with the optimized AST.
     */
    public IBindingSet[] getOptimizedASTBindingSets() {

        return (IBindingSet[]) getProperty(Annotations.OPTIMIZED_AST_BINDING_SETS);

    }

    /**
     * Set the original AST model (before any optimizations).
     */
    public void setOriginalAST(final QueryRoot queryRoot) {
        
        setProperty(Annotations.ORIGINAL_AST, queryRoot);

    }

    /**
     * Return the optimized AST model.
     */
    public QueryRoot getOptimizedAST() {

        return (QueryRoot) getProperty(Annotations.OPTIMIZED_AST);

    }

    /**
     * Set the query parse time in nanoseconds.
     */
    public void setQueryParseTime(final Long parseTime) {
        
        setProperty(Annotations.QUERY_PARSE_TIME, parseTime);

    }
    
    /**
     * Get the query parse time in nanoseconds.
     */
    public Long getQueryParseTime() {

        return (Long) getProperty(Annotations.QUERY_PARSE_TIME);

    }

    /**
     * Set the RDF value resolution time in nanoseconds.
     */
    public void setResolveValuesTime(final Long nanos) {
        
        setProperty(Annotations.RESOLVE_VALUES_TIME, nanos);

    }
    
    /**
     * Get the resolve values time in nanoseconds.
     */
    public Long getResolveValuesTime() {

        return (Long) getProperty(Annotations.RESOLVE_VALUES_TIME);

    }

    /**
     * Set the optimized AST model.
     * <p>
     * Note: You MUST deep copy the original AST to avoid destructive side
     * effects when the {@link IASTOptimizer}s are run.
     */
    public void setOptimizedAST(final QueryRoot queryRoot) {
        
        setProperty(Annotations.OPTIMIZED_AST, queryRoot);

    }

    /**
     * Clears the optimized AST model (necessary when something on which it
     * depends has been changed in the original AST model, for example, if you
     * replace the {@link DatasetNode}).
     */
    public void clearOptimizedAST() {

        clearProperty(Annotations.OPTIMIZED_AST);
           
    }
    
    /**
     * Return the physical query plan (pipeline bops).
     */
    public PipelineOp getQueryPlan() {

        return (PipelineOp) getProperty(Annotations.QUERY_PLAN);

    }

    /**
     * Set the physical plan for query or update (pipeline bops).
     */
    public void setQueryPlan(final PipelineOp queryPlan) {
        
        setProperty(Annotations.QUERY_PLAN, queryPlan);

    }
    
    public String toString() {
        
        final StringBuilder sb = new StringBuilder();

        final String queryString = getQueryString();
        
        final Object parseTree = getParseTree();

        /*
         * Note: Resolve the original AST without regard to query versus update.
         */
        final ASTBase originalAST = (ASTBase) getProperty(Annotations.ORIGINAL_AST);

        /*
         * Note: Resolve the optimized AST without regard to query versus
         * update.
         */
        final ASTBase optimizedAST = (ASTBase) getProperty(Annotations.OPTIMIZED_AST);

        final PipelineOp queryPlan = getQueryPlan();

        if (queryString != null) {

            sb.append("\n");
            sb.append(Annotations.QUERY_STRING);
            sb.append("\n");
            sb.append(queryString);
            sb.append("\n");

        }
        
        if (parseTree != null) {

            sb.append("\n");
            sb.append(Annotations.PARSE_TREE);
            sb.append("\n");

            if(parseTree instanceof SimpleNode) {

                // Dump parse tree for sparql.jjt grammar.
                sb.append(((SimpleNode)parseTree).dump(""));
                
            } else {
            
                /*
                 * Dump some other parse tree, assuming it implements toString()
                 * as pretty print.
                 */
                sb.append(parseTree.toString());
                sb.append("\n");
                
            }

        }

        if (originalAST != null) {

            sb.append("\n");
            sb.append(Annotations.ORIGINAL_AST);
            sb.append(originalAST);

        }

        if (optimizedAST != null) {

            sb.append("\n");
            sb.append(Annotations.OPTIMIZED_AST);
            sb.append(optimizedAST);

        }

        if (queryPlan != null) {

            sb.append("\n");
            sb.append(Annotations.QUERY_PLAN);
            sb.append("\n");
            sb.append(BOpUtility.toString(queryPlan));

        }

        return sb.toString();

    }

}
